/* vcc/main.c 
 * 
 * This file is part of vcc. 
 * 
 * vcc is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * vcc is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with vcc. If not, see <https://www.gnu.org/licenses/>
 */ 


#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS 1
#endif

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>

#include <vcc/vcc.h>
#include <vcc/version.h>
#include <vcc/pretty.h>
#include <vcc/socket.h>

#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")
#endif

#define DEFAULT_RATE 	1000


int send_msg(const char *msg, const char *usrname);
int read_msg(struct vcc_request *buf);
int get_msgs(void);
int vcc_login(char *, char *);

int do_cmd(char *s);
int do_bh(struct vcc_request *);


int rate;
socket_t fd;
char *empty_package;
char usrname[USRNAME_SIZE];
static char *reading;
static int size_read;

static int usage(void) {
	fprintf(stderr, "Usage: vcc [server]\n");
	fprintf(stderr, "As default, server is 124.223.105.230. \n");

	return 1;
}


static socket_t init_socket(const char *server_addr) {
	struct sockaddr_in addr, server;
#ifdef _WIN32
	WSADATA wsa_data;
	if (unlikely(WSAStartup(MAKEWORD(1, 0), &wsa_data) != 0)) {
		fprintf(stderr, "not supported platform\n");
		exit(1);
	}

	if (unlikely((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)) {
#else
	if (unlikely((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)) {
#endif
		perror("socket()");

		exit(1);
	}

	memset(&addr, 0, sizeof(struct sockaddr_in));

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(0);

	if (unlikely(bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0)) {
		perror("bind()");

		exit(1);
	}

	memset(&server, 0, sizeof(struct sockaddr_in));

	server.sin_family = AF_INET;
	server.sin_port = htons(VCC_PORT);
#ifdef _WIN32
	server.sin_addr.s_addr = inet_addr(server_addr);
	if (unlikely(server.sin_addr.s_addr == INADDR_NONE)) {
#else
	if (unlikely(!inet_aton(server_addr, &server.sin_addr))) {
#endif
		fprintf(stderr, "invalid ip address. "
				"leave blank if you want to connect the default server");

		exit(1);
	}

	if (unlikely(connect(fd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0)) {
		perror("connect()");

		exit(1);
	}
	return fd;
}
static int login(void) {
	char *passwd;

	fprintf(stderr, "login as: ");
	scanf("%s", usrname);
#ifdef _WIN32
	printf("password: ");
	passwd = malloc(100);
	scanf("%s", passwd);
#else
	passwd = getpass("password: ");
#endif

	vcc_login(usrname, passwd);
#ifdef _WIN32
	free(passwd);
#endif
	return 0;
}

int main(int argc, const char **argv) {
	struct pollfd 		fds[2];
	struct vcc_request 	*req;
	char 			*buf;
	int 			n, size;

	setvbuf(stdout, NULL, _IONBF, BUFSIZ);

	if (unlikely(argc != 1 && argc != 2)) 
		return usage();

	printf(VCCD_VERSION "\n");

	init_socket(argv[1] ? argv[1] : "124.223.105.230");
	printf("Connected to server. \n");

	login();

	fds[0].fd = 0;
	fds[0].events = POLLIN;
	fds[0].revents = 0;

	fds[1].fd = fd;
	fds[1].events = POLLIN;
	fds[1].revents = 0;

	rate = DEFAULT_RATE;

	if (unlikely(!(buf = malloc(MSG_SIZE)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return 1;
	}

	if (unlikely(!(reading = malloc(REQ_SIZE)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return 1;
	}

	size_read = 0;

	for (;;) {
#ifdef _WIN32
		n = WSAPoll(fds, 2, rate);
		if (n == SOCKET_ERROR) {
#else
		n = poll(fds, 2, rate);
		if (n == -1) {
#endif
			if (likely(errno == EINTR)) 
				continue;

			perror("poll()");

			break;
		}

		if (!n) {
			/* 1 sec timed out */

			get_msgs();

			continue;
		}

		if (fds[0].revents & POLLIN) {
			/* read keyboard */

			memset(buf, 0, MSG_SIZE);
			scanf("%s", buf);

			if (*buf == '-') 
				do_cmd(buf);

			else if (*buf != '\n') {
				send_msg(buf, usrname);

				pretty_new_msg(usrname, buf);
				printf("\n");
			}

			pretty_prompt(usrname);
		}

		if (fds[1].revents & POLLIN) {
			size = recv(fd, reading + size_read, REQ_SIZE, 0);
			size_read += size;

			if (size_read < REQ_SIZE) 
				continue;

			size_read = 0;
			req = (struct vcc_request *) reading;

			if (ntohl(req->reqtype) != REQ_MSG_NEW) {
				do_bh(req);

				continue;
			}

			buf = req->msg;

			if (!*buf || *(unsigned char *) buf == 0xffu) 
				continue;

			pretty_new_msg(req->usrname, buf);
			pretty_prompt(usrname);
		}
	}

	printf("bye. \n");

	return 0;
}

